Тут буду писать небольшие заметки по производительности в Node.js (и фрейморках), если кто-то желает присоединиться - добро пожаловать.
Map_вместо обычных объектовПри работе с ассоциативными массивами многие используют обычные объекты {}. Однако Map работает быстрее при частых операциях поиска и вставки:
const map = new Map();
map.set("key", "value");
console.log(map.get("key"));
В отличие от обычных объектов, Map сохраняет порядок вставки и работает быстрее при большом количестве записей.
Часто в коде можно встретить последовательные вызовы await, которые замедляют выполнение:
const user = await getUser();
const posts = await getPosts(user.id);
Оптимизируем параллельным выполнением:
const [user, posts] = await Promise.all([getUser(), getPosts(user.id)]);
Streams) для обработки данныхПри работе с большими файлами не загружайте их полностью в память:
const fs = require("fs");
fs.createReadStream("bigfile.txt").pipe(process.stdout);
Это позволит читать и обрабатывать данные по частям, снижая потребление памяти.
При итерации по массивам используйте for вместо forEach для максимальной производительности:
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
Это работает быстрее, так как избегает создания дополнительных замыканий. Иной раз forEach может быть быстрее, т.к. является методом к массиву.
Если функция выполняет дорогостоящие операции, кэшируй результаты:
const cache = new Map();
function expensiveOperation(input: string) {
if (cache.has(input)) return cache.get(input);
const result = compute(input);
cache.set(input, result);
return result;
}
Иной раз может быть не корректым, но использовать Redis или внешний источник правды это маст хэв практис.
Node.js использует синхронную загрузку модулей. Разнесённые require() по коду мешают прогнозируемости и задерживают исполнение. Лучше загружать все зависимости в начале, чтобы избежать неожиданных блокировок и ускорить startup.
Каждый вызов .bind() создаёт новую функцию:
// Плохо
someArray.forEach(this.handle.bind(this));
// Лучше
const handler = (x) => this.handle(x);
someArray.forEach(handler);
Меньше аллокаций, особенно в циклах, особенно во вложенных циклах.
Это создаёт объект без прототипа:
const map = Object.create(null);
map["key"] = "value";
Нет hasOwnProperty, toString, и других “лишних” свойств. Полезно, когда используешь объект как хэш-карту.
nextTick() выполняется до любых I/O. Если ты вызываешь process.nextTick() внутри самого nextTick, ты создаёшь бесконечную микро-очередь, блокирующую все I/O события:
function loop() {
process.nextTick(loop); // Может заблокировать event loop
}
-Jame